﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MentalAlchemy.Molecules.MachineLearning
{
	/// <summary>
	/// [molecule]
	/// 
	/// Coevolutionary algorithm for optimization problems.
	/// </summary>
	public class CoevolutionaryAlgorithm : EvolutionaryAlgorithm
	{
		protected EvolutionaryAlgorithm[] eas;	// sub-algorithms.

		/// <summary>
		/// Sizes of subproblems.
		/// </summary>
		public int[] SubproblemSizes
		{
			get
			{
				if (eas == null) return null;

				// find out subproblem sizes.
				var res = new int[eas.Length];
				for (int i = 0; i < eas.Length; i++)
				{
					res[i] = eas[i].Population[0].Size;
				}

				return res;
			}
			set
			{
				eas = new EvolutionaryAlgorithm[value.Length];
			}
		}

		public override void Init(EAParameters parameters)
		{
			base.Init(parameters);

			for (int i = 0; i < eas.Length; i++)
			{
				var ea = eas[i];
				var tempParam = parameters;
				tempParam.IndividualSize = SubproblemSizes[i];
				ea.Init(tempParam);
			}
		}

		public override void Continue(EAParameters parameters)
		{
			//
			// validate [parameters].
			if (FitnessFunction != null && !EAElements.ValidateParameters(parameters)) throw new Exception("[AmalgamIDEA.Continue]: Invalid parameters setting or fitness function is undefined.");

			for (CurrentGeneration = 1; CurrentGeneration <= parameters.GenerationsNumber; ++CurrentGeneration)
			{
				Evaluate();
				selPopul = EAElements.TournamentSelection(popul, parameters.TournamentSize, parameters.RNG);
				popul = Crossing(selPopul, parameters);
				Mutation(popul, parameters);

				// implement elitism via substitution of the 1st child with the best individual found.
				popul[0] = bestInd.Clone();
			}

			// todo: fix
			Evaluate();	// final population evaluation.
		}

		/// <summary>
		///  Evaluates population and updates [stats].
		/// </summary>
		public override void Evaluate()
		{
			// perform evaluation and (if required) replace current best individual.
			Individual tempBest;
			foreach (var ea in eas)
			{
				// todo: create special evaluation for coevolutionary algorithm.
				throw new NotImplementedException();
				Evaluation(ea.Population, FitnessFunction, out tempBest);
				if (bestInd == null || FitnessComparator.IsBetter(tempBest.Fitness, bestInd.Fitness)) bestInd = (Individual)tempBest.Clone();
			}

			CollectStats();
		}
	}
}
